// System Unit
// Exception Handling Stubs

(*************)
(* Interface *)
(*************)
{$IFDEF Include_Modules_Header}

 Type
  PExceptionRecord = ^TExceptionRecord;
  TExceptionRecord = Record
   ExceptionCode: Cardinal;
   ExceptionFlags: Cardinal;
   ExceptionRecord: PExceptionRecord;
   ExceptionAddress: Pointer;
   NumberParameters: Cardinal;
   Case {OSExcept:}Boolean of
    False: ( ExceptionInformation: Array [0..14] of NativeUInt ); { OS Exception }
    True : ( ExceptAddr: Pointer; ExceptObject: Pointer );        { Delphi Exception }
  END;

  {$POINTERMATH ON}
  PExcDescEntry = ^TExcDescEntry;
  TExcDescEntry = Record
   vTable:  Pointer;
   Handler: Pointer;
  END;
  {$POINTERMATH OFF}

  PExceptionHandlerDesc = ^TExceptionHandlerDesc;
  TExceptionHandlerDesc = Packed Record
   JMP: Byte;
   HandlerAddr: Pointer;
   Case Boolean of
    False: ( RealHandler: Byte );                                         { Try ... Except Descripter }
    True:  ( Cnt: Integer; ExcTab: {Array [0..Cnt-1] of} TExcDescEntry ); { Try ... OnExcept Descripter }
  END;

  PExceptionRegistry = ^TExceptionRegistry;
  TExceptionRegistry = Record
   Next: PExceptionRegistry;
   Desc: PExceptionHandlerDesc;
   EBP: Pointer;
   Case Integer of
    0:  ( );                            { Try ... Except/Finally Registry }
    1:  ( ConstructedObject: Pointer ); { ClassCreate Registry }
    2:  ( SelfOfMethod: Pointer );      { SafeCall Registry }
  END;

 // Compiler Magic Function
 Procedure _RaiseExcept;
 Type RTLProc_RaiseExcept = Procedure(Var Obj: Pointer{TObject});

 // Compiler Magic Function
 Procedure _HandleOnException;
 //Type RTLProc_HandleOnException = RTLProc_HandleException;

 // Compiler Magic Function
 Procedure _HandleAutoException;
 //Type RTLProc_HandleAutoException = RTLProc_HandleException;

 // Compiler Magic Function
 Procedure _HandleAnyException;
 //Type RTLProc_HandleAnyException = RTLProc_HandleException;

 // Compiler Magic Function
 Procedure _HandleFinally;
 //Type RTLProc_HandleFinally = RTLProc_HandleException;

 Type RTLProc_HandleException = Procedure(Var ExcRec: TExceptionRecord; ExcReg: PExceptionRegistry; Const Context, Dispatch: Pointer); StdCall;

 // Compiler Magic Function
 Procedure _DoneExcept;
 //Type RTLProc_DoneExcept = TProc; // The declaration is dependant on the processing of Handle*Exception

 // Compiler Magic Function
 Procedure _RaiseAgain;
 //Type RTLProc_RaiseAgain = TProc; // The declaration is dependant on the processing of Handle*Exception

 Type RTLProc_LogException = Procedure(Context: PChar = NIL);
 Var LogException: RTLProc_LogException = NIL;

 Var
  DebugHook: Byte = 0; { 1 to notify debugger of non-Delphi exceptions
                        >1 to notify debugger of exception unwinding }

 //==================
 // RTL Extensions
 //==================
 Type
  TRTLEx_Exception = Record
   _RaiseExcept: RTLProc_RaiseExcept;
   _HandleOnException: RTLProc_HandleException;
   _HandleAutoException: RTLProc_HandleException;
   _HandleAnyException: RTLProc_HandleException;
   _HandleFinally: RTLProc_HandleException;
   _DoneExcept: TProc;
   _RaiseAgain: TProc;
   LogException: RTLProc_LogException;
  END;

 Procedure RTLExtend_Exception(Var Ex: TRTLEx_Exception);

{$ENDIF Include_Modules_Header}
(******************)
(* Implementation *)
(******************)
{$IFDEF Include_Modules_Implement}

 ResourceString
  _RTL_ResStr_UnknownExc         = 'Unknown external exception, code $$$$$$$$';
  _RTL_ResStr_ExceptInExcept     = 'exception processing block (old exception discarded)';
  _RTL_ResStr_NoExceptionContext = 'Not in exception processing context';
  _RTL_ResStr_NoDefaultHandler   = 'Default exception handler not installed';

 Function _INT_ExcRuntimeError(Const ExceptionCode: Cardinal): TRuntimeError;

  Procedure _LOC_LogUnknownException(Const ExcCode: Cardinal);
   Var
    ErrorMsg: Array [0..RTL_ResStr_MsgLen] of Char;
    MsgLen: Cardinal;
   BEGIN
    MsgLen:= LoadResStringFx(@_RTL_ResStr_UnknownExc, ErrorMsg, RTL_ResStr_MsgLen+1);
    RTL_CardinalToHex8(ExcCode, @ErrorMsg[MsgLen-8]);
    Log(llWarn, ErrorMsg);
   END;

  Const
   EXCEPTION_ACCESS_VIOLATION         = $C0000005;
   EXCEPTION_ARRAY_BOUNDS_EXCEEDED    = $C000008C;
   EXCEPTION_BREAKPOINT               = $80000003;
   EXCEPTION_DATATYPE_MISALIGNMENT    = $80000002;
   EXCEPTION_FLT_DENORMAL_OPERAND     = $C000008D;
   EXCEPTION_FLT_DIVIDE_BY_ZERO       = $C000008E;
   EXCEPTION_FLT_INEXACT_RESULT       = $C000008F;
   EXCEPTION_FLT_INVALID_OPERATION    = $C0000090;
   EXCEPTION_FLT_OVERFLOW             = $C0000091;
   EXCEPTION_FLT_STACK_CHECK          = $C0000092;
   EXCEPTION_FLT_UNDERFLOW            = $C0000093;
   EXCEPTION_GUARD_PAGE               = $80000001;
   EXCEPTION_ILLEGAL_INSTRUCTION      = $C000001D;
   EXCEPTION_IN_PAGE_ERROR            = $C0000006;
   EXCEPTION_INT_DIVIDE_BY_ZERO       = $C0000094;
   EXCEPTION_INT_OVERFLOW             = $C0000095;
   EXCEPTION_INVALID_DISPOSITION      = $C0000026;
   EXCEPTION_INVALID_UNWIND           = $C0000027;
   EXCEPTION_INVALID_HANDLE           = $C0000008;
   EXCEPTION_NONCONTINUABLE_EXCEPTION = $C0000025;
   EXCEPTION_PRIV_INSTRUCTION         = $C0000096;
   EXCEPTION_SINGLE_STEP              = $80000004;
   EXCEPTION_STACK_OVERFLOW           = $C00000FD;
  BEGIN
   Case ExceptionCode of
    EXCEPTION_ACCESS_VIOLATION:         Result:= reExcAccessViolation;
    EXCEPTION_ARRAY_BOUNDS_EXCEEDED:    Result:= reExcArrayBounds;
    EXCEPTION_BREAKPOINT:               Result:= reExcBreakPoint;
    EXCEPTION_DATATYPE_MISALIGNMENT:    Result:= reExcDataAlign;
    EXCEPTION_FLT_DENORMAL_OPERAND:     Result:= reExcFPDenormal;
    EXCEPTION_FLT_DIVIDE_BY_ZERO:       Result:= reExcFPDivZero;
    EXCEPTION_FLT_INEXACT_RESULT:       Result:= reExcFPInexact;
    EXCEPTION_FLT_INVALID_OPERATION:    Result:= reExcFPInvalidOp;
    EXCEPTION_FLT_OVERFLOW:             Result:= reExcFPOverflow;
    EXCEPTION_FLT_STACK_CHECK:          Result:= reExcFPStackOverflow;
    EXCEPTION_FLT_UNDERFLOW:            Result:= reExcFPUnderflow;
    EXCEPTION_GUARD_PAGE:               Result:= reExcGuardPage;
    EXCEPTION_ILLEGAL_INSTRUCTION:      Result:= reExcBadInst;
    EXCEPTION_IN_PAGE_ERROR:            Result:= reExcPageError;
    EXCEPTION_INT_DIVIDE_BY_ZERO:       Result:= reExcIntDivZero;
    EXCEPTION_INT_OVERFLOW:             Result:= reExcIntOverflow;
    EXCEPTION_INVALID_DISPOSITION:      Result:= reExcInvalidDisp;
    EXCEPTION_INVALID_UNWIND:           Result:= reExcInvalidUnwind;
    EXCEPTION_INVALID_HANDLE:           Result:= reExcInvalidHandle;
    EXCEPTION_NONCONTINUABLE_EXCEPTION: Result:= reExcNonContinue;
    EXCEPTION_PRIV_INSTRUCTION:         Result:= reExcPrivInst;
    EXCEPTION_SINGLE_STEP:              Result:= reExcSingleStep;
    EXCEPTION_STACK_OVERFLOW:           Result:= reExcStackOverflow;
    else // Map any other exception code to unhandled exception
     BEGIN
      _LOC_LogUnknownException(ExceptionCode);
      Result:= reUnhandledException;
     END;
   END;
  END;

 Const
  EXCEPTION_UNWINDING = 2;
  EXCEPTION_EXIT_UNWIND = 4;
  EXCEPTION_UNWIND_CONTEXT = EXCEPTION_UNWINDING OR EXCEPTION_EXIT_UNWIND;

  EXCHANDLER_CONTINUE_SEARCH = 1;

 Procedure _INT_LogNestedExcept(BlockStr: PChar);
  Var
   ErrContext: Array [0..RTL_ResStr_MsgLen+RTL_ResStr_CapLen+1] of Char;
   BlockStrEnd: PChar;
  BEGIN
   BlockStrEnd:= RTL_PWideCharCopyNPtr(ErrContext, BlockStr, RTL_ResStr_CapLen);
   BlockStrEnd[0]:= ' ';
   LoadResStringFx(@_RTL_ResStr_ExceptInExcept, BlockStrEnd+1, RTL_ResStr_MsgLen+1);
   LogException(ErrContext);
  END;

 ThreadVar
  _INT_ExceptionContext: Boolean;

 Function _INT_DummyHandler(Var ExcRec: TExceptionRecord; ExcReg: PExceptionRegistry; Const Context, Dispatch: Pointer): Integer; StdCall;
  BEGIN
   Result:= EXCHANDLER_CONTINUE_SEARCH;
  END;

 // Generic Re-raise function
 Procedure _INT_DoReraise(Const RaiseFlags: NativeUInt; Const ExcReg: PExceptionRegistry; Const IntExcHandler, &EBP: Pointer); StdCall; Forward;

 Procedure _INT_Reraise;
  // (Internal RetAddr)  +4    (Return from HandleAnyException::DoExceptBlock)
  // (Except RetAddr)    [ESP]
  ASM
   MOV [ESP+4], 0

   // (Raise Flags)     +4
   // (Except RetAddr)  [ESP]
   JMP _INT_DoReraise
  END;

 Procedure _RTL_RaiseExcept;
  ASM
   JMP _INT_NotImplemented
  END;

 Procedure _RTL_HandleOnException;
  ASM
   JMP _INT_NotImplemented
  END;

 Procedure _RTL_HandleAutoException;
  ASM
   JMP _INT_NotImplemented
  END;

 Procedure _RTL_HandleAnyException(Var ExcRec: TExceptionRecord; ExcReg: PExceptionRegistry; Const Context, Dispatch: Pointer); StdCall;
  // (Saved EBP)                  \  <-- [Old EBP]
  // (Exception Block)-------\     > Try Exception Registry
  // (PrevExcReg) <-------\  |    /
  // ???                  |  |            +-------------------------+
  // ...                  |  \----------> | JMP _HandleAnyException |
  // ???                  |               | (Real Handler)          | <-- (Exec Addr)
  // (Dispatch)           | +20   \       | ......                  |
  // (Context)            | +16   |
  // (Excption Registry)--+ +12    > OS Call Frame
  // (Exception Record)     +8    |
  // (OS RetAddr)           +4    /
  // (Saved EBP)            [EBP]
  Procedure DoExceptBlock(Const &EBP: Pointer; Const HandlerPtr: Pointer);
   ASM
    // Pull out except block address
    MOV ECX, [HandlerPtr]
    // Ground except block
    MOV [HandlerPtr], Offset _INT_DummyHandler
    // Calculate real handler address
    LEA EDX, [ECX+TExceptionHandlerDesc.RealHandler]
    // Restore EBP
    MOV EBP, &EBP
    // "Return" to handler
    JMP EDX
   END;
  BEGIN
   With ExcRec do
    BEGIN
     IF ExceptionCode = _INT_RTLErrorException
      then _INT_RTLError:= _INT_PRTLErrorRec(@ExceptionInformation[0])^
      else // Not an RTLError Exception
       With _INT_RTLError do
        BEGIN
         Flags:= 0;
         Error:= _INT_ExcRuntimeError(ExceptionCode);
        END;
     ExceptionFlags:= ExceptionFlags OR EXCEPTION_UNWINDING;
    END;

   ExcReg:= _WIN_RtlUnwind(ExcReg, NIL, ExcRec, ExcReg);

   // Enter exception handling context
   _INT_ExceptionContext:= True;
   Try
    DoExceptBlock(ExcReg.&EBP, @ExcReg.Desc);
    // Will not return if no exception
   Except
    _INT_LogNestedExcept('Except');
    _INT_Reraise;
   END;
  END;

 Procedure _RTL_DoneExcept(Const InternalRet: Pointer; Const ExcReg: PExceptionRegistry; Const IntExcHandler, &EBP: Pointer); StdCall;
  // (Saved EBP)                    \
  // (Exception Block)               > Try Exception Registry
  // (PrevExcReg) <----\            /  <-- [Exception Registry]
  // ???               |
  // ...               |
  // ???               |               <-- Old ESP
  // (Saved EBP)       |            \  <-- [Old EBP]
  // (Exception Block) |             > Try Exception Registry (Done)
  // (PrevExcReg)------/ <--\       /
  // ???                    |
  // ...                    |
  // ???                    |
  // (Dispatch)             |       \
  // (Context)              |       |
  // (Excption Registry)----+        > OS Call Frame
  // (Exception Record)     |       |
  // (OS RetAddr)           |       /
  // (Saved EBP)            |
  // ???                    |
  // ...                    |
  // ???                    |
  // (Saved EBP)            | +16   \
  // (Internal Handler)     | +12    > Internal Exception Registry
  // (Excption Registry)----/ +8    /
  // (Internal RetAddr)       +4    (Return from HandleAnyException::DoExceptBlock)
  // (Except RetAddr)         [ESP]    <-- [Return Addr]
  // (Saved EBP)              [EBP]
  Procedure DoneExcept(Const PrevExcReg: PExceptionRegistry; Const EBPPtr: Pointer);
   ASM
    // Pop exception registration
    XOR  ECX, ECX
    MOV  FS:[ECX], PrevExcReg

    // Restore ESP
    MOV  ESP, EBPPtr
    // Pull out except block return address
    MOV  EAX, [EBP+4]
    // Restore EBP
    POP  EBP

    // "Return" to resume except block
    JMP  EAX
   END;
  BEGIN
   // Leave exception handling context
   _INT_ExceptionContext:= False;
   _INT_ResetError;

   // Note that technically we need to call RTLUnwind to release the exception registrations (internal + try).
   // But, since we knew there is nothing to release, we can safely "skip" these exception registration.

   DoneExcept(ExcReg.Next, @ExcReg.&EBP);
  END;

 Procedure _INT_DoReraise(Const RaiseFlags: NativeUInt; Const ExcReg: PExceptionRegistry; Const IntExcHandler, &EBP: Pointer); StdCall;
  // (Saved EBP)                   \
  // (Exception Block)              > Try Exception Registry
  // (PrevExcReg) <-------\        /  <-- [Exception Registry]
  // ???                  |
  // ...                  |
  // ???                  |           <-- Old ESP
  // (Dispatch)           | +20    \
  // (Context)            | +16    |
  // (Excption Registry)--+ +12     > OS Call Frame
  // (Exception Record)   | +8     |
  // (OS RetAddr)         | +4     /  <-- [Return Addr]
  // (Saved EBP) <-\      | [&EBP]    <-- [Old EBP]
  // ???           |      |
  // ...           |      |
  // ???           |      |
  // (Saved EBP)---/      | +16    \
  // (Internal Handler)   | +12     > Internal Exception Registry
  // (Excption Registry)--/ +8     /
  // (Raise Flags)          +4
  // (Except RetAddr)       [ESP]
  Function GetExcRec(Const EBPPtr: Pointer): PExceptionRecord;
   ASM
    MOV EAX, [EBPPtr+8]
   END;
  Procedure ContinueExcept(Const ExcReg: PExceptionRegistry; Const EBPPtr: Pointer);
   ASM
    // Pop exception registration
    XOR  ECX, ECX
    MOV  FS:[ECX], ExcReg

    // Restore ESP
    MOV  ESP, EBPPtr
    // Restore EBP
    POP  EBP

    // Set return value
    MOV  EAX, EXCHANDLER_CONTINUE_SEARCH

    // Directly return to OS
    // Note that the stack isn't quite aligned, but that is OK
    // The NT exception dispatcher will correct the stack automatically
   END;
  BEGIN
   // Leave exception handling context
   _INT_ExceptionContext:= False;

   With GetExcRec(&EBP)^ do
    BEGIN
     ExceptionCode:= _INT_RTLErrorException;
     ExceptionFlags:= ExceptionFlags AND NOT EXCEPTION_UNWINDING;
     NumberParameters:= 2;
     With _INT_PRTLErrorRec(@ExceptionInformation[0])^ do
      BEGIN
       Error:= _INT_RTLError.Error;
       Flags:= _INT_RTLError.Flags OR RaiseFlags;
      END;
    END;

   // Note that technically we need to call RTLUnwind to release the (internal) exception registration.
   // But, since we knew there is nothing to release, we can safely "skip" this exception registration.

   ContinueExcept(ExcReg, &EBP);
  END;

 Procedure _RTL_RaiseAgain;
  // (Internal RetAddr)  +4    (Return from HandleAnyException::DoExceptBlock)
  // (Except RetAddr)    [ESP]
  ASM
   MOV [ESP+4], _INT_ErrorFlag_Reraise

   // (Raise Flags)     +4
   // (Except RetAddr)  [ESP]
   JMP _INT_DoReraise
  END;

 Function _RTL_HandleFinally(Var ExcRec: TExceptionRecord; ExcReg: PExceptionRegistry; Const Context, Dispatch: Pointer): Integer; StdCall;
  // (Saved EBP)                  \  <-- [Old EBP]
  // (Exception Block)-------\     > Try Exception Registry
  // (PrevExcReg) <-------\  |    /
  // ???                  |  |            +-------------------------+
  // ...                  |  \----------> | JMP _HandleAnyException |
  // ???                  |               | CALL (Finally Block)    | <-- (Exec Addr)
  // (Dispatch)           | +20   \       | ......                  |
  // (Context)            | +16   |
  // (Excption Registry)--+ +12    > OS Call Frame
  // (Exception Record)     +8    |
  // (OS RetAddr)           +4    /
  // (Saved EBP)            [EBP]
  Procedure DoFinallyBlock(Const &EBP: Pointer; Const HandlerPtr: Pointer);
   ASM
    // Pull out except block address
    MOV ECX, [HandlerPtr]
    // Ground except block
    MOV [HandlerPtr], Offset _INT_DummyHandler
    // Calculate real handler address
    LEA EDX, [ECX+TExceptionHandlerDesc.RealHandler]
    // Restore EBP
    MOV EBP, &EBP
    // Call handler
    CALL EDX
   END;
  BEGIN
   Try
    DoFinallyBlock(ExcReg.&EBP, @ExcReg.Desc);
   Except
    _INT_LogNestedExcept('Finally');
    _INT_Reraise;
   END;

   Result:= EXCHANDLER_CONTINUE_SEARCH;
  END;

 Procedure _RTL_LogException(Context: PChar);
  BEGIN
   IF _INT_ExceptionContext then
    BEGIN
     IF Assigned(Context) OR (_INT_RTLError.Flags AND _INT_ErrorFlag_Reraise = 0)
     then _INT_LogRTLError(_INT_RTLError, llError, Context);
    END
   else _INT_LogResMsg(llWarn, @_RTL_ResStr_NoExceptionContext);
  END;

 Procedure _INT_LogExceptionResContext(Const ResContext: PResStringRec);
  Var Context: Array [0..RTL_ResStr_MsgLen] of Char;
  BEGIN
   LoadResStringFx(ResContext, Context, RTL_ResStr_MsgLen+1);
   LogException(Context);
  END;

 Var
  _INT_MainExceptionRegistration: PExceptionRegistry = NIL;

 // Delphi compiler pre-allocates some space on the stack at the start of executable / DLL entry point
 //  so we can install a "grand" exception handler in case all other handlers fail to catch
 // * Must be called from a caller *without* its own stack frame (notice the EBP reference)
 Procedure _INT_SetDefaultExcHandler(Const ExcHandler: TProc);
 // (Free Space)        [EBP-4]..[EBP-16]
 // Saved EBP           [EBP]
 // OSRetAddr           +4
  Function GetDefaultExcReg: PExceptionRegistry;
   ASM
    LEA  EAX, [EBP-Type(TExceptionRegistry)+Type(Pointer)]
   END;
  Procedure InstallExcReg(Const ExcReg: PExceptionRegistry);
   ASM
    XOR  ECX, ECX
    MOV  EDX, FS:[ECX]
    MOV  [ExcReg].TExceptionRegistry.Next, EDX
    MOV  FS:[ECX], ExcReg
   END;
  Procedure InternalExcHandler;//(Var ExcRec: TExceptionRecord; ExcReg: PExceptionRegistry; Const Context, Dispatch: Pointer); StdCall;
   ASM
    JMP  _HandleAnyException

    // (Default Exception Handler)     <-- [Return Addr]
    // (Saved EBP)                  \  <-- [EBP]
    // (Exception Block)             > Default Exception Registry
    // (PrevExcReg) <-------\       /
    // ???                  |
    // ...                  |
    // ???                  |
    // (Dispatch)           |       \
    // (Context)            |       |
    // (Excption Registry)--+        > OS Call Frame
    // (Exception Record)   |       |
    // (OS RetAddr)         |       /
    // (Saved EBP)          |
    // ???                  |
    // ...                  |
    // ???                  |
    // (Saved EBP)          | +12   \
    // (Internal Handler)   | +8     > Internal Exception Registry
    // (Excption Registry)--/ +4    /
    // (Internal RetAddr)     [ESP] (Return from HandleAnyException::DoExceptBlock)

    // Note that technically we need to call RTLUnwind to release the exception registrations (internal + default).
    // But, since we knew there is nothing to release, we can safely "skip" these exception registration.

    // Pop exception registration
    MOV  ESP, [ESP+4]
    XOR  EAX, EAX
    POP  ECX  // PrevExcReg
    POP  EDX  // @InternalExcHandler (discarded)
    POP  EDX
    MOV  FS:[EAX], ECX

    // Now we "return" to the default exception handler
   END;
  BEGIN
   IF Assigned(_INT_MainExceptionRegistration)
   then Error(reInvalidOp);

   _INT_MainExceptionRegistration:= GetDefaultExcReg;
   With _INT_MainExceptionRegistration^ do
    BEGIN
     Desc:= @InternalExcHandler;
     &EBP:= SelfOfMethod;
     SelfOfMethod:= @ExcHandler;
     // OldExcHandler       -8    \
     // InternalExcHandler  -4     > Exception Registration
     // Saved EBP           [EBP] /
     // [ExcHandler]        +4
     // OSRetAddr           +8
    END;
   InstallExcReg(_INT_MainExceptionRegistration);
  END;

 Var
  RTL_RaiseExcept: Pointer = NIL;
  RTL_HandleOnException: Pointer = NIL;
  RTL_HandleAutoException: Pointer = NIL;
  RTL_HandleAnyException: Pointer = @_RTL_HandleAnyException;
  RTL_DoneExcept: Pointer = @_RTL_DoneExcept;
  RTL_RaiseAgain: Pointer = @_RTL_RaiseAgain;
  RTL_HandleFinally: Pointer = @_RTL_HandleFinally;

 Procedure _RaiseExcept;
  ASM
   JMP RTL_RaiseExcept
  END;

 Procedure _HandleOnException;
  ASM
   JMP RTL_HandleOnException
  END;

 Procedure _HandleAutoException;
  ASM
   JMP RTL_HandleAutoException
  END;

 Procedure _HandleAnyException;
  ASM
   JMP RTL_HandleAnyException
  END;

 Procedure _DoneExcept;
  ASM
   JMP RTL_DoneExcept
  END;

 Procedure _RaiseAgain;
  ASM
   JMP RTL_RaiseAgain
  END;

 Procedure _HandleFinally;
  ASM
   JMP RTL_HandleFinally
  END;

 //==================
 // RTL Extensions
 //==================

 Procedure RTLExtend_Exception(Var Ex: TRTLEx_Exception);
  BEGIN
   _INT_RTL_DoEx(RTL_RaiseExcept, @Ex._RaiseExcept);
   _INT_RTL_DoEx(RTL_HandleOnException, @Ex._HandleOnException);
   _INT_RTL_DoEx(RTL_HandleAutoException, @Ex._HandleAutoException);
   _INT_RTL_DoEx(RTL_HandleAnyException, @Ex._HandleAnyException);
   _INT_RTL_DoEx(RTL_DoneExcept, @Ex._DoneExcept);
   _INT_RTL_DoEx(RTL_RaiseAgain, @Ex._RaiseAgain);
   _INT_RTL_DoEx(RTL_HandleFinally, @Ex._HandleFinally);
   _INT_RTL_DoEx(@LogException, @Ex.LogException);
  END;

 Procedure _INT_Exception_Init;
  BEGIN
   LogException:= _RTL_LogException;
   // RTL_RaiseExcept : Already Initialized
   // RTL_HandleOnException : Already Initialized
   // RTL_HandleAutoException : Already Initialized
   // RTL_HandleAnyException : Already Initialized
   // RTL_DoneExcept : Already Initialized
   // RTL_RaiseAgain : Already Initialized
   // RTL_HandleFinally : Already Initialized
  END;

{$ENDIF Include_Modules_Implement}
(******************)
(* Initialization *)
(******************)
{$IFDEF Include_Modules_Initialize}
{$ENDIF Include_Modules_Initialize}
(****************)
(* Finalization *)
(****************)
{$IFDEF Include_Modules_Finalize}
{$ENDIF Include_Modules_Finalize}
